package org.promote.hotspot.client.filter;

import lombok.extern.slf4j.Slf4j;
import org.promote.hotspot.client.ClientContext;
import org.promote.hotspot.client.cache.CacheFactory;
import org.promote.hotspot.client.cache.LocalCache;
import org.promote.hotspot.client.model.ValueModel;
import org.promote.hotspot.client.collector.HotCollectorFactory;
import org.promote.hotspot.client.model.KeyHotModel;
import org.promote.hotspot.client.rule.RuleChangeSubscriber;
import org.promote.hotspot.common.config.KeyType;
import org.promote.hotspot.common.model.HotKeyModel;
import org.promote.hotspot.common.tool.SystemClock;

import java.util.concurrent.atomic.LongAdder;

/**
 * @author enping.jep
 * @date 2023/11/28 21:10
 **/
@Slf4j
public class HotSpotDetector {

    /**
     * 判断是否是热点
     * 如果不在规则内，返回false
     * 如果没有被缓存上或者缓存了但即将过期，则返回true
     *
     * @param key
     * @return
     */
    public static boolean isHotKey(String key) {
        //从本地缓存中获取对应的键值对
        LocalCache cache = RuleChangeSubscriber.findByKey(key);
        //判断该key是否命中了规则
        if (cache == null) {
            return false;
        }
        //一定符合规则，该热点恰好属于管理规则约束的情况
        //没有缓存在本地jvm，则直接采集 isNotCached为true，意味着没有被本地jvm缓存
        ValueModel valueModel = (ValueModel) cache.get(key);
//        ValueModel valueModel = getValueSimple(key);
        boolean isHot = valueModel != null;//判断是否存在于本地缓存，如果存在，则说明是热点数据
        // 没缓存，则直接采集
        if (!isHot || isNearExpire(valueModel)) {
            HotCollectorFactory.getHotKeyCollector().collect(buildHotKeyModel(key));
        }
        //统计计数，获取统计信息，规则命中的总访问次数、 以及成为热点后的访问次数,便于规则的调优
        HotCollectorFactory.getHotCountColletor().collect(new KeyHotModel(key, isHot));
        return isHot;
    }

    /**
     * 仅获取value，如果不存在也不上报热key
     */
    static ValueModel getValueSimple(String key) {
        Object object = getCache(key).get(key);
        if (object == null) {
            return null;
        }
        return (ValueModel) object;
    }

    /**
     * 判断这个key是否在被探测的规则范围内
     * true是指命中了规则
     * false是没有命中规则
     */
    private static boolean inRule(String key) {
        return RuleChangeSubscriber.findByKey(key) != null;
    }

    private static LocalCache getCache(String key) {
        return CacheFactory.getNonNullCache(key);
    }

    private static HotKeyModel buildHotKeyModel(String key) {
        HotKeyModel hotKeyModel = new HotKeyModel();
        hotKeyModel.setAppName(ClientContext.APP_NAME);
        LongAdder cnt = new LongAdder();
        cnt.add(1);
        hotKeyModel.setCount(cnt);
        hotKeyModel.setRemove(false);
        hotKeyModel.setKeyType(KeyType.REDIS_KEY);
        hotKeyModel.setKey(key);
        return hotKeyModel;
    }

    /**
     * 是否临近过期
     */
    private static boolean isNearExpire(ValueModel valueModel) {
        //判断是否过期时间小于2秒，小于2秒的话也发送
        if (valueModel == null) {
            return true;
        }
        return valueModel.getCreateTime() + valueModel.getDuration() - SystemClock.now() <= 2000;
    }

    /**
     * 判断是否是热key。适用于只需要判断key，而不需要value的场景
     */
    public static boolean isHot(String key) {
        return getValueSimple(key) != null;
    }

    /**
     * 纯粹的本地缓存
     */
    public static void setValueDirectly(String key, Object value) {
        getCache(key).set(key, value);
    }
}
